package ude.univ.esiag.gon.uchiha.register;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;

import javax.rmi.PortableRemoteObject;

import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;

import ude.univ.esiag.gon.uchiha.operation.Operation;
import ude.univ.esiag.gon.uchiha.subscriber.Subscriber;
import ude.univ.esiag.gon.uchiha.subscriber.impl.SubscriberImpl;
import ude.univ.esiag.gon.uchiha.subscriber.impl._SubscriberImpl_Tie;
import ude.univ.esiag.gon.uchiha.topic.Topic;
import ude.univ.esiag.gon.uchiha.writer.DataWriter;

public class Multicaster {
	private HashMap<String, Topic> iors;
	private static Multicaster singleton;
	private ORB orb;
	private POA rootPOA;
	private Multicaster(){
		iors = new HashMap<String, Topic>();
		Properties pro = new Properties();
		String host = "localhost";//getHost();
		
		pro.put("com.sun.CORBA.ORBServerHost", host);
		
		orb = ORB.init(new String[]{}, pro);
		
		try{
			rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
			rootPOA.the_POAManager().activate();
		}catch(Exception e){
			e.printStackTrace();
		}
		new Thread(new Runnable() {
			
			public void run() {
				orb.run();
			}
		}).start();
	}
	public void startUp(){
		new Thread(new Runnable() {
			
			public void run() {
				listening();
			}
		}).start();
		new Thread(new Runnable() {
			
			public void run() {
				listeningClient();
			}
		}).start();
	}
	public Topic getTopicByName(String name){
		return iors.get(name);
	}
	public String getHost(){
		try{
			String interfaceS = "wlan0";
			NetworkInterface inter = NetworkInterface.getByName(interfaceS);
			Enumeration<InetAddress> adres = inter.getInetAddresses();
			String x = "";
			while(adres.hasMoreElements()){
				x = adres.nextElement().getHostAddress();
			}
			return x;
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}
	public static Multicaster getInstance(){
		if(singleton == null)
			singleton = new Multicaster();
		return singleton;
	}
	public void registerRoot(Topic topic){
		register(topic);//not yet until he bootstrap
		SubscriberImpl subscriber = topic.getSubscriber();
		String ior = subscriber_to_ior(subscriber);
		topic.setSubscriberIOR(ior);
		broadcast(topic.getName(), ior);
	}
	public String subscriber_to_ior(SubscriberImpl subscriber){
		String ior = null;
		_SubscriberImpl_Tie tie = new _SubscriberImpl_Tie();
		tie.setTarget(subscriber);
		try{
			Object objRef = rootPOA.servant_to_reference(tie);
			ior = orb.object_to_string(objRef);
		}catch(Exception e){
			e.printStackTrace();
		}
		return ior;
	}
	public Subscriber ior_to_subscriber(String ior){
		Object objRef = orb.string_to_object(ior);
		return (Subscriber)PortableRemoteObject.narrow(objRef, Subscriber.class);
	}
	public void register(Topic topic){
		iors.put(topic.getName(), topic);
	}
	public void broadcast(String name, String ior){
		try{
			byte[] buf =(name + "/" + ior).getBytes();
			InetAddress address = InetAddress.getByName("228.5.6.7");
			DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 9091);
			DatagramSocket socket = new DatagramSocket();
			socket.send(packet);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public void listeningClient(){
		try{
			MulticastSocket socket = new MulticastSocket(9092);
			socket.joinGroup(InetAddress.getByName("228.5.6.7"));
			byte[] buf = new byte[1024];
			DatagramPacket packet = new DatagramPacket(buf, buf.length);
			while(true){
				socket.receive(packet);
				String broadcast = new String(packet.getData()).trim();
				StringTokenizer tokens = new StringTokenizer(broadcast, "/");
				String topicName = tokens.nextToken();
				String ior = tokens.nextToken();
				String rootName = topicName;
				int index = topicName.indexOf(".");
				if(index != -1){
					rootName = topicName.substring(0, index);
				}
				// recuperer le topic root
				Topic topic = iors.get(rootName);
				
				
				Subscriber subscriber = ior_to_subscriber(ior);
				
				DataWriter dw = new DataWriter();
				dw.addSubscriber(subscriber);

				// recuperer le topic fils voulud
				topic = topic.getTopic(topicName);
				
				if(topic == null){
					Operation operation = new Operation(topicName, "Topic Introuvable");
					operation.setType(Operation.ERROR);
					operation.setTarget(Operation.TOPIC);
					operation.setToReplicate(false);
					dw.write(operation);
				}
				else{
					String ior1 = topic.getSubscriberIOR();
					if(ior1 == null){
						ior1 = subscriber_to_ior(topic.getSubscriber());
						topic.setSubscriberIOR(ior1);
					}
					Operation operation = new Operation(topic.getName(), ior1);
					operation.setType(Operation.IOR);
					operation.setTarget(Operation.TOPIC);
					operation.setToReplicate(false);
					dw.write(operation);
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public void listening(){
		try{
			MulticastSocket socket = new MulticastSocket(9091);
			socket.joinGroup(InetAddress.getByName("228.5.6.7"));
			while(true){
				byte[] buf = new byte[1024];
				DatagramPacket packet = new DatagramPacket(buf, buf.length);
				socket.receive(packet);
				String broadcast = new String(packet.getData()).trim();
				
				StringTokenizer tokens = new StringTokenizer(broadcast, "/");
				String topicName = tokens.nextToken();
				String ior = tokens.nextToken();
				Topic topic = iors.get(topicName);
				if(topic != null){
					String ior1 = topic.getSubscriberIOR();
					if(!ior1.equals(ior)){
						Subscriber subscriber = ior_to_subscriber(ior);
						topic.getWriter().addSubscriber(subscriber);
						
						DataWriter dw = new DataWriter();
						dw.addSubscriber(subscriber);
						
						Operation operation = new Operation(topic.getName(), topic.getSubscriberIOR());
						operation.setType(Operation.IOR);
						operation.setTarget(Operation.TOPIC);
						operation.setToReplicate(false);
						
						topic.getWriter().write(operation);
						
						operation.getData().setObj(topic.createTopicS());
						operation.setType(Operation.BOOTSTRAP);
						operation.setToReplicate(false);
						dw.write(operation);
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}
